/* Copyright Thanh-Trung Pham, 2015 */

package jp.ac.uaizu.control;

import jp.ac.jaist.control.RMPIController;
import jp.ac.jaist.input.RMPIIAWrapper;
import jp.ac.jaist.input.RMPIInputModeler;
import jp.ac.jaist.simulation.RMPIReliabilitySimulationListener;
import jp.ac.jaist.simulation.RMPIReliabilitySimulationStatistics;
import jp.ac.jaist.simulation.RMPIReliabilitySimulator;
import jp.ac.uaizu.translation.InputModel;

import java.util.*;

/**
 * @author Thanh-Trung Pham
 */
public class SerCompSimulationHelper implements RMPIReliabilitySimulationListener {
    private SerCompPredictionWork m_oPredictionWork;

    private RMPIReliabilitySimulator m_oRMPIReliabilitySimulator;
    private Timer m_oTimer;

    /**
     * The constructor.
     *
     * @param predictionWork the prediction work.
     */
    public SerCompSimulationHelper(SerCompPredictionWork predictionWork) {
        m_oPredictionWork = predictionWork;
    }

    /**
     * Run the simulations.
     */
    public void runSimulations() {
        InputModel oInputModel = m_oPredictionWork.getInputModel();

        RunConfigurationSettings oRunConfigurationSettings = oInputModel.getRunConfigurationSettings();
        int iNumberOfSimulationsRequired = oRunConfigurationSettings.getNumberOfSimulationsRequired();
        int iSimulationDepth = oRunConfigurationSettings.getSimulationDepth();
        int iSimulationTime = oRunConfigurationSettings.getSimulationTime();

        RMPIController oRMPIController = RMPIController.newInstance();
        for (int i = 0; i < iNumberOfSimulationsRequired; i++) {
            if (iSimulationDepth == 0) {
                iSimulationDepth = Integer.MAX_VALUE;
            }

            m_oRMPIReliabilitySimulator = oRMPIController.conductReliabilitySimulation(m_oPredictionWork.getRMPIInputModeler(), iSimulationDepth, new RMPIReliabilitySimulationListener[]{this});

            setUpTimer(iSimulationTime);

            try {
                m_oRMPIReliabilitySimulator.join();
            } catch (InterruptedException oEx) {
                //do nothing
            }
        }

        aggregateSimulationResults();
    }

    /**
     * Aggregate all the simulation results.
     */
    private void aggregateSimulationResults() {
        RMPIInputModeler oRMPIInputModeler = m_oPredictionWork.getRMPIInputModeler();

        Set<Set<String>> oAFS = oRMPIInputModeler.getAFS();
        Set<Set<String>> oAIOS = oRMPIInputModeler.getAIOS();

        List<RMPIReliabilitySimulationStatistics> oAllSimulationStatistics = m_oPredictionWork.getAllSimulationStatistics();
        int iNumberOfSimulations = oAllSimulationStatistics.size();

        RMPIIAWrapper[] oRMPIIAWrappers = new RMPIIAWrapper[iNumberOfSimulations];
        for (int i = 0; i < iNumberOfSimulations; i++) {
            RMPIReliabilitySimulationStatistics oSimulationStatistics = oAllSimulationStatistics.get(i);
            int iToTalNumberOfRuns = oSimulationStatistics.getToTalNumberOfRuns();

            oRMPIIAWrappers[i] = new RMPIIAWrapper(oRMPIInputModeler, true);
            for (Set<String> oF : oAFS) {
                oRMPIIAWrappers[i].setProbability(RMPIInputModeler.RMPI_F0_AS_SET, oF, ((double) oSimulationStatistics.getCounter(oF)) / iToTalNumberOfRuns);
            }

            for (Set<String> oO : oAIOS) {
                oRMPIIAWrappers[i].setProbability(RMPIInputModeler.RMPI_F0_AS_SET, oO, ((double) oSimulationStatistics.getCounter(oO)) / iToTalNumberOfRuns);
            }
        }

        RMPIIAWrapper oAggregatedIAWrapper = new RMPIIAWrapper(oRMPIInputModeler, true);
        for (Set<String> oF : oAFS) {
            double dProbability = 0;
            for (int i = 0; i < iNumberOfSimulations; i++) {
                dProbability += oRMPIIAWrappers[i].getProbability(RMPIInputModeler.RMPI_F0_AS_SET, oF);
            }

            oAggregatedIAWrapper.setProbability(RMPIInputModeler.RMPI_F0_AS_SET, oF, dProbability/iNumberOfSimulations);
        }

        for (Set<String> oO : oAIOS) {
            double dProbability = 0;
            for (int i = 0; i < iNumberOfSimulations; i++) {
                dProbability += oRMPIIAWrappers[i].getProbability(RMPIInputModeler.RMPI_F0_AS_SET, oO);
            }

            oAggregatedIAWrapper.setProbability(RMPIInputModeler.RMPI_F0_AS_SET, oO, dProbability/iNumberOfSimulations);
        }

        m_oPredictionWork.setRMPIIAWrapper(oAggregatedIAWrapper);
    }

    /**
     * Set up the timer.
     *
     * @param iSimulationTime the simulation time in minutes.
     */
    private void setUpTimer(int iSimulationTime) {
        if (iSimulationTime > 0) {
            m_oTimer = new Timer();
            m_oTimer.schedule(new TimerTask() {
                @Override
                public void run() {
                    m_oRMPIReliabilitySimulator.setStopped(true);

                }
            }, new Date(System.currentTimeMillis() + iSimulationTime * 60 * 1000));

        }
    }

    /**
     * Stop the timer.
     */
    private void stopTimer() {
        if (m_oTimer != null) {
            m_oTimer.cancel();
        }
    }

    @Override
    public void informProgress(int numberOfCompletedRuns) {
        //do nothing
    }

    @Override
    public void informCompletion(RMPIReliabilitySimulationStatistics rmpiReliabilitySimulationStatistics) {
        m_oPredictionWork.addRMPIReliabilitySimulationStatistics(rmpiReliabilitySimulationStatistics);
        stopTimer();


    }

}
